from flask import abort, jsonify
from flask import current_app
from flask import g
from flask import request
from flask import session
from info import constants, db
from info.models import News, User, Comment, CommentLike
from info.utlis.response_code import RET
from . import news_bp
from flask import render_template
from info.utlis.common import login_user_data


# 用户评论新闻
@news_bp.route('/news_comment',methods=['post'])
@login_user_data
def news_comment():
    """评论接口（主评论 子评论）"""
    """
        1.获取参数
            1.1 用户对象 新闻id 评论内容 parent_id(主评论id)
        2.校验参数
            2.1 非空判断
            2.2 用户是否登录判断
        3.逻辑处理
            3.1 根据新闻id查询该新闻（新闻存在才能去评论）
            3.2 创建评论模型 给各个属性赋值
            3.3 判断parent_id是否有值
            3.4 将模型对象保存到数据库
        4.返回值处理
    """
    #1.获取参数
        #1.1 用户对象 新闻id 评论内容 parent_id(主评论id)
    params_dict = request.json
    news_id = params_dict.get("news_id")
    comment = params_dict.get("comment")
    parent_id = params_dict.get("parent_id")
    user = g.user
    #2.校验参数
        #2.1 非空判断
    if not all([news_id,comment]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数不足")
        #2.2 用户是否登录判断
    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")
    #3.逻辑处理
         #3.1 根据新闻id查询该新闻（新闻存在才能去评论）
    try:
        news = News.query.get(news_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询新闻数据异常")
    if not news:
        return jsonify(errno=RET.NODATA,errmsg="新闻不存在")

        #3.2 创建评论模型 给各个属性赋值
    comment_obj = Comment()
    comment_obj.user_id = user.id
    comment_obj.news_id = news_id
    comment_obj.content = comment
        #3.3 判断parent_id是否有值
    if parent_id:
        comment_obj.parent_id = parent_id

        #3.4 将模型对象保存到数据库
    try:
        db.session.add(comment_obj)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg="提交用户新闻评论异常")

    return jsonify(errno=RET.OK,errmsg="评论成功",data=comment_obj.to_dict())

# 点赞取消点赞
@news_bp.route('/comment_like',methods=['post'])
@login_user_data
# 整体来说该接口接受前端传来的数据对数据库的数据进行更改，并返回传来的数据有没出错信息
def comment_like():
    """评论的点赞、取消点赞"""
    """
    1.获取参数
        1.1 用户对象 新闻id 评论id action(点赞、取消点赞)
    2.校验参数
        2.1 非空判断
        2.2 用户是否登录判断
        2.3 action in ["add", "remove"]
    3.逻辑处理
        3.1 根据评论id获取评论模型对象（只有评论存在的时候你才能去点赞、取消点赞）
        3.2 跟action去完成点赞或者取消点赞的功能
        3.3 点赞： 根据 user_id和comment_id 查询CommentLike是否存在
            3.3.1 CommentLike存在：表示用户点过这条评论
            3.3.2 CommentLike不存在：新建CommentLike模型对象最终保存回数据库
            3.3.3 记录comment模型的点赞数量（+1）
        3.4 取消点赞：根据 user_id和comment_id 查询CommentLike是否存在
            CommentLike存在才取消点赞
            记录comment模型的点赞数量（-1）

    4.返回值处理
    """
    #1.获取参数
        #1.1 用户对象 新闻id 评论id action(点赞、取消点赞)
    params_dict = request.json
    news_id = params_dict.get("news_id")
    comment_id = params_dict.get("comment_id")
    action = params_dict.get("action")
    user = g.user
    #2.校验参数
        #2.1 非空判断
    if not all([news_id, comment_id, action]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
        #2.2 用户是否登录判断
    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")
        #2.3 action in ["add", "remove"]
    if action not in ["add", "remove"]:
        return jsonify(errno=RET.PARAMERR,errmsg="action数据填写错误")

    #3.逻辑处理
        #3.1 根据评论id获取评论模型对象（只有评论存在的时候你才能去点赞、取消点赞）
    try:
        comment = Comment.query.get(comment_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询评论数据异常")
    if not comment:
        return jsonify(errno=RET.NODATA,errmsg="用户不存在")
        #3.2 根据action去完成点赞或者取消点赞的功能
    # 用户要做点赞操作
    if action == "add":
        # 用户和评论之间有个点赞的链接表，
        # 查询在该表中有没用户ID为user_id用户点赞了评论ID为comment_id的评论
        # 没有就代表该用户与该评论没有建立链接表，就是该用户没有点赞该评论
        comment_like = CommentLike.query.filter(CommentLike.comment_id==comment_id,
                                    CommentLike.user_id==user.id).first()
        # 评论表中的user_id是哪个用户评论时自己需要增加的字段值，然后评论完多一条评论就是多一个comment_id，
        # 意思就是该评论是哪个用户评论的
        # 而CommentLike表是把已经存在的表和用户进行连接，表示的意思就是用户对原有的评论进行点赞操作
        # 虽然两表的user_id都是同样的名字，但是只要看需求哪个表的字段跟哪个表的字段有联系时进行表关联操作就行
        if not comment_like:
            comment_like = CommentLike()
            comment_like.user_id = user.id
            comment_like.comment_id = comment_id
            db.session.add(comment_like)
            comment.like_count += 1
    else:
        comment_like = CommentLike.query.filter(CommentLike.comment_id == comment_id,
                                    CommentLike.user_id == user.id).first()

        if comment_like:
            db.session.delete(comment_like)
            comment.like_count -= 1

    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg="保存数据异常")

    return jsonify(errno=RET.OK,errmsg="OK")


# 收藏和取消收藏
@news_bp.route('/news_collect',methods=['post'])
@login_user_data
def news_collect():
    """收藏和取消收藏接口"""
    """
    1.获取参数
        1.1 用户对象 新闻id action：（收藏or取消收藏）
    2.校验参数
        2.1 非空判断
        2.2 用户是否登录判断
        2.3 action值的判断 action in ["collect", "cancel_collect"]
    3.逻辑处理
        3.1 根据新闻id查询该新闻
        3.2 取消收藏：将新闻对象从collection_news列表中移除
        3.3 收藏： 将新闻添加到collection_news中
        3.4 提交修改操作到数据库
    4.返回值处理
    """
#   1.获取参数
        #1.1 用户对象 新闻id action：（收藏or取消收藏）
    parms_dict = request.json
    news_id = parms_dict.get("news_id")
    action = parms_dict.get("action")
    user = g.user
    #2.校验参数
        #2.1 非空判断
    if not all([news_id,action]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数不足")
        #2.2 用户是否登录判断
    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")

        #2.3 action值的判断 action in ["collect", "cancel_collect"]
    if action not in ["collect","cancel_collect"]:
        return jsonify(errno=RET.PARAMERR,errmsg="参数填写错误")

    #3.逻辑处理
        #3.1 根据新闻id查询该新闻
    try:
        news = News.query.get(news_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询新闻对象异常")
    if not news:
        return jsonify(errno=RET.NODATA,errmsg="新闻不存在")

        # 3.2 取消收藏：将新闻对象从collection_news列表中移除
    # 表明用户要做取消收藏行为
    if action == "cancel_collect":
        user.collection_news.remove(news)
    else:
        user.collection_news.append(news)

    # 3.4 提交修改操作到数据库
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="提交用户收藏的新闻数据失败")
    #4.返回值处理
    return jsonify(errno=RET.OK,errmsg="OK")


# 关注和取消关注
@news_bp.route('/followed_user',methods=['post'])
@login_user_data
def followed_user():
    # 作者id
    user_id = request.json.get("user_id")
    action = request.json.get("action")
    user = g.user

    if not all([user_id,action]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数不足")

    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")

    if action not in ["unfollow","follow"]:
        return jsonify(errno=RET.PARAMERR,errmsg="action参数有误")

    try:
        author = User.query.get(user_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询新闻作者异常")

    if not author:
        return jsonify(errno=RET.NODATA,errmsg="该作者不存在")

    if action == "follow":
        # 如果作者不在用户的偶像列表中
        if author not in user.followed:
            user.followed.append(author)
        else:
            return jsonify(errno=RET.DATAEXIST,errmsg="用户已经关注过该作者")

    if action == "unfollow":
        if author in user.followed:
            user.followed.remove(author)
        else:
            return jsonify(errno=RET.DATAEXIST,errmsg="用户已经取消关注过该作者")

    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg="提交用户偶像数据异常")

    return jsonify(errno=RET.OK,errmsg="OK")


# 文章详情页的数据展示
@news_bp.route('/<int:news_id>')
@login_user_data
# 不传参数会报错.点击哪条新闻，index.js就会从循环中取出该新闻字典的id
def news_detail(news_id):
    # # -------1.当用户登录成功后可以使用session对象获取里面的用户user_id -------
    # user_id = session.get("user_id", "")
    # user = None  # type:User
    # try:
    #     if user_id:
    #         user = User.query.get(user_id)
    # except Exception as e:
    #     current_app.logger.error(e)
    #     #   不用返回给前端，不能用return返回错误数据
    user = g.user

        # -------2.查询新闻的点击排行数据 -------
        # 1.根据浏览量查询6条点击排行的新闻数据
    try:
        new_model_list = News.query.order_by(News.clicks.desc()).limit(constants.CLICK_RANK_MAX_NEWS)
    except Exception as e:
        current_app.looger.error(e)
    # 把对象列表转为字典列表
    new_dict_list = []
    for news in new_model_list if new_model_list else []:
        new_dict_list.append(news.to_dict())


    # -------3.查询新闻的详情数据 -------
    try:
        # TODO:注意
        news = News.query.get(news_id)
    except Exception as e:
        current_app.logger.error(e)
    if not news:
        abort(404)

    # 浏览量自增
    news.clicks += 1

    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        abort(404)

    # -------4.查询用户是否收藏过该新闻 -------
    # True: 用户收藏过该条新闻 False:用户未收藏该新闻
    is_collected = False
    if user:
        if news in user.collection_news:
            is_collected = True

    # -------5.查询用户是否关注过该新闻作者 -------
    # True: 用户过关注该条作者 False:用户未关注该作者
    is_followed = False
    if news:
        try:
            user1 = User.query.get(news.user_id)
        except Exception as e:
            current_app.logger.error(e)
    user1 = None
    if user:
        if user1 in user.followed:
            is_followed = True

    #  -------6.查询该新闻的评论数据 -------
    comments = []
    try:
        comments = Comment.query.filter_by(news_id=news_id).order_by(Comment.create_time.desc())
    except Exception as e:
        current_app.logger.error(e)



    # -------7.需求：查询当前用户在当前新闻的评论里边具体点赞了那几条评论 -------
    # 1. 查询出当前新闻的所有评论，取得所有评论的id —>  list[1,2,3,4,5,6]
    comment_id_list = []
    if user:
        comment_id_like = [comment.id for comment in comments]
        # 2.再通过评论点赞模型(CommentLike)查询当前用户点赞了哪几条评论  —>[模型1,模型2...]
        commentlike_model_like = CommentLike.query.filter(CommentLike.user_id == user.id,
                                 CommentLike.comment_id.in_(comment_id_like)).all()

        comment_id_list = [commentlike.comment_id for commentlike in commentlike_model_like]


    # 当前新闻的所有评论对象转字典
    comment_dict_list = []
    for comment in comments if comments else []:
        comment_dict = comment.to_dict()
        # 评论点赞的标志位,增加一个键值对
        comment_dict["is_like"] = False

        # 该新闻所有的评论中，该用户点赞的评论is_like就等于True
        if user and (comment.id in comment_id_list):
            comment_dict["is_like"] = True

        comment_dict_list.append(comment_dict)

    data = {
        # 该登录用户对象的字典
        "user_info": user.to_dict() if user else None,
        # 包含所有新闻字典的列表
        "newsClickList": new_dict_list,
        # 该新闻对象的字典
        "news": news.to_dict(),
        "is_collected": is_collected,
        "comments":comment_dict_list,
        "is_followed":is_followed
    }

    return render_template('news/detail.html',data=data)